package com.oocl.scan.handler;

import com.oocl.scan.dao.ComponentDao;
import com.oocl.scan.dao.impl.ComponentDaoImpl;
import com.oocl.scan.model.Component;
import com.oocl.scan.model.JavaClassElement;
import com.oocl.scan.model.Relationship;
import com.oocl.scan.utils.ScanConstants;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;

public class DaoAndModelHandler extends BaseHandler{

  public static final String PERSIST = ".persist(";
  public static final String REMOVE = ".remove(";

  @Override
  public void handle(JavaClassElement javaClassElement) {
    if (!isInterface(javaClassElement) && isDao(javaClassElement.getClassName(), javaClassElement.getPackageDeclaration().getName().toString())) {
      List<MethodDeclaration> methodDeclarationList = javaClassElement.getMethodDeclarationList();
      for (int i = 0; i < methodDeclarationList.size(); i++) {
        if (!methodDeclarationList.get(i).isConstructor()) {
          Type returnType = methodDeclarationList.get(i).getReturnType2();
          if (returnType != null) {
            if (!ScanConstants.METHOD_RETURN_TYPE_VOID.equals(returnType.toString())) {
              handleReturnModelCase(javaClassElement, returnType);
            } else {
              handlePersistCase(javaClassElement, methodDeclarationList.get(i));
            }
          }
        }
      }
    }
  }

  private Map<String, String> buildVariableTypeAneNameMap(List<Statement> statementList) {
    Map<String, String> variableTypeAneName = new HashMap<>();
    statementList.forEach((statement -> {
      if (statement instanceof VariableDeclarationStatement) {
        VariableDeclarationStatement variableDeclarationStatement = (VariableDeclarationStatement) statement;
        String variableType = ((VariableDeclarationStatement) statement).getType().toString();
        String variableName = ((VariableDeclarationFragment) variableDeclarationStatement.fragments().get(0)).getName().getIdentifier();
        variableTypeAneName.put(variableName, variableType);
      }
      if (statement instanceof EnhancedForStatement) {
        EnhancedForStatement enhancedForStatement = (EnhancedForStatement) statement;
        String parameterType = enhancedForStatement.getParameter().getType().toString();
        String parameterName = enhancedForStatement.getParameter().toString().substring(enhancedForStatement.getParameter().toString().indexOf(" ") + 1);
        variableTypeAneName.put(parameterName, parameterType);
      }
    }));
    return variableTypeAneName;
  }

  private List<String> getModelForPersistOrRemoveMethod(List<Statement> statementList, Map<String, String> variableTypeAneNameMap) {
    List<String> modelList = new ArrayList<>();
    if (variableTypeAneNameMap.size() != 0) {
      statementList.forEach((statement -> {
        if (isPersistOrRemove(statement.toString())) {
          String parameter = getPersistOrRemoveParameter(statement.toString());
          String model = variableTypeAneNameMap.get(parameter);
          if (model != null) {
            modelList.add(model);
          }
        }
      }));
    }
    return modelList;
  }

  private String getPersistOrRemoveParameter(String statementString) {
    String str = "";
    if (statementString.contains(PERSIST)) {
      str = statementString.substring(statementString.indexOf(PERSIST));
    }
    if (statementString.contains(REMOVE)) {
      str = statementString.substring(statementString.indexOf(REMOVE));
    }
    str = str.substring(str.indexOf("(") + 1, str.indexOf(")"));
    return str;
  }

  private boolean isPersistOrRemove(String statementString) {
    if (statementString.contains(PERSIST) || statementString.contains(REMOVE)) {
      return true;
    }
    return false;
  }

  private void handlePersistCase(JavaClassElement javaClassElement, MethodDeclaration methodDeclaration) {
    if (methodDeclaration.getBody() != null && methodDeclaration.getBody().statements() != null) {
      List<Statement> statementList = methodDeclaration.getBody().statements();
      Map<String, String> variableTypeAneNameMap = buildVariableTypeAneNameMap(statementList);
      variableTypeAneNameMap.putAll(buildParameterTypeAneNameMap(methodDeclaration.parameters()));
      List<String> modelList = getModelForPersistOrRemoveMethod(statementList, variableTypeAneNameMap);
      modelList.forEach(modelName -> {
        String modelClassName = getFullModelClassName(javaClassElement.getImports(), modelName);
        if (modelClassName != null) {
          buildModelAndDaoRelationship(modelClassName, javaClassElement);
        }
      });
    }
  }

  private Map<String, String> buildParameterTypeAneNameMap(List parameters) {
    Map<String, String> variableTypeAneNameMap = new HashMap<>();
    parameters.forEach(parameter -> {
      String parameterType = parameter.toString().substring(0, parameter.toString().indexOf(" "));
      String parameterName = parameter.toString().substring(parameter.toString().indexOf(" ") + 1);
      variableTypeAneNameMap.put(parameterName, parameterType);
    });
    return variableTypeAneNameMap;
  }

  private void handleReturnModelCase(JavaClassElement javaClassElement, Type returnType) {
    if (returnType instanceof ParameterizedType) {
      List arguments = ((ParameterizedType) returnType).typeArguments();
      for (Object argument : arguments) {
        String model = argument.toString();
        String modelClassName = getFullModelClassName(javaClassElement.getImports(), model);
        buildModelAndDaoRelationship(modelClassName, javaClassElement);
      }
    } else {
      String modelClassName = getFullModelClassName(javaClassElement.getImports(), returnType.toString());
      buildModelAndDaoRelationship(modelClassName, javaClassElement);
    }
  }

  private String getFullModelClassName(List importList, String modelName) {
    String modelFullClassName = null;
    for (int j = 0; j < importList.size(); j++) {
      String importClass = getClassNameFromImport(importList.get(j).toString());
      if (importClass.endsWith(modelName) && isIRISClassName(importClass) && !isFrmPersistenceIEntityManager(importClass)) {
        modelFullClassName = importClass;
        break;
      }
    }
    return modelFullClassName;
  }

  private void buildModelAndDaoRelationship(String modelName, JavaClassElement element) {
    if (modelName != null) {
      ComponentDao dao = new ComponentDaoImpl();
      Component modelComponent = dao.findByName(modelName);
      if (modelComponent != null) {
        Relationship relationship = new Relationship(ScanConstants.RELATION_TYPE_FLOWS, element.getJavaClassFullName(), element.getModuleName(), modelName,
            modelComponent.getModule());
        addToRelationshipStore(relationship);
      }
    }
  }

  private boolean isInterface(JavaClassElement javaClassElement) {
    return javaClassElement.getTypeDeclaration().isInterface();
  }

  private boolean isIRISClassName(String className) {
    return className.contains(ScanConstants.COM_OOCL);
  }

  private boolean isFrmPersistenceIEntityManager(String className) {
    return className.endsWith(ScanConstants.FRM_PERSISTENCE_I_ENTITY_MANAGER);
  }

  private String getClassNameFromImport(String importClass) {
    return importClass.substring(importClass.indexOf(" ") + 1, importClass.indexOf(";"));
  }
}
